Today we are releasing version 2.2.0 of the Couchbase .NET SDK. The major focus for this release is feature compatibility with Couchbase Server 4.0 GA which was also released today! The core features in this release are:
- N1QL support
- Multidimensional Scaling (MDS) support
- Enhanced durability
- GEO Spatial Views
Along with those major features, the 2.2.0 SDK also contains several bug fixes and improvements. If you are using an earlier version of the SDK, it's strongly suggested that you upgrade to this latest version.
Full release notes are available here.
N1QL: A new SQLlike query language for JSON
Without a doubt the mos significant feature released with Couchbase Server 4.0 is complete support for querying JSON documents with N1QL, a new SQL like query language for documents. Version 2.2.0 of Couchbase .NET SDK fully supports N1QL through a new client API.
The QueryRequest class
The QueryRequest class represents a N1QL query request to Couchbase Server. It encapsulates the N1QL REST API over port 8093 and provides a type-safe interface for executing queries. Along with the internal query client built into the CouchbaseBucket class, it handles a lot of the plumbing and complexity of the REST API, for example:
- Retry logic temporary and recoverable failures
- Uniform distribution of queries throughout the cluster
- Logic for handling prepared statements
- Uri caching
- Mapping query results to POCOs
- et al
The QueryRequest class separates the actual N1QL statement from the execution, error handling and data mapping of the results of the query. Here is an example of using the QueryRequest to build a request that will later be executed against the CouchbaseBucket:
1 2 3 4 5 |
var queryRequest = new QueryRequest() .Statement("SELECT * FROM `beer-sample` LIMIT $1") .AddPositionalParameter(10) .Timeout(new TimeSpan(0, 0, 0, 500)); |
First we create the QueryRequest object and pass in a N1QL statement into it. Then we add a positional parameter (note names parameters are also supported). Finally we override the global ClientConfiguration.QueryRequestTimeout value with a custom timeout of 500 milliseconds.
Once we have a QueryRequest created we can execute it against a CouchbaseBucket using the Query method:
1 2 3 4 5 6 7 8 9 |
var result = await bucket.QueryAsync<dynamic>(queryRequest); if(result.Success) { foreach(var doc in result.Rows) { Console.Writeline(doc); } } |
That's it! The new API has the following methods and properties exposed by QueryRequest:
Method | Description | Optional |
---|---|---|
Statement | Sets a N1QL statement to be executed. | false |
Prepared | Sets a N1QL statement to be executed in an optimized way using the given queryPlan. | true |
Timeout | Sets the maximum time to spend on the request. | true |
ReadOnly | If a GET request, this will always be true otherwise false. | true |
Metrics | Specifies that metrics should be returned with query results. | true |
AddNamedParameter | Adds a named parameter to the parameters to the statement or prepared statement. | true |
AddPositionalParameters | Adds a positional parameter to the parameters to the statement or prepared statement. | true |
ScanConsistency | Specifies the consistency guarantee/constraint for index scanning. | true |
AddHoc | If set to false, the client will try to perform optimizations transparently based on the server capabilities, like preparing the statement and then executing a query plan instead of the raw query. | true |
These are the pragmatic and fully supported parameters. In addition there are parameters/methods for Compression, Format and Encoding, but I do not suggest they are used at this time. Also, ScanConsistency of of AtPlus and StatementPlus are not currently supported and will throw a NotSupportedException if passed in.
The QueryResult class
Once a request has been sent to server and processed, a response will be returned back to the client in the form of a JSON document. The client will take the response and map it to the QueryRequest class. The client will also analyze the status codes of the response (both N1QL and HTTP) and determine if the request should be retried or not. In general, any HTTP 400 errors will not be retried they will be returned back to the caller because then indicate a client issue.
The notable properties and methods of the QueryRequest class are as follows:
Method | Description |
---|---|
Success | True if query was successful. |
Message | Optional message returned by query engine or client |
Exception | If Success is false and an exception has been caught internally, this field will contain the exception. |
RequestId | Gets the request identifier. |
ClientContextId | Gets the clientContextID of the request, if one was supplied. Used for debugging. |
Signature | Gets the schema of the results. Present only when the query completes successfully. |
Rows | Gets a list of all the objects returned by the query. An object can be any JSON value. |
Status | Gets the status of the request; possible values are: success, running, errors, completed, stopped, timeout, fatal. |
Errors | Gets a list of 0 or more error objects; if an error occurred during processing of the request, it will be represented by an error object in this list. |
Warnings | Gets a list of 0 or more warning objects; if a warning occurred during processing of the request, it will be represented by a warning object in this list. |
Metrics | Gets an object containing metrics about the request. |
HttpStatusCode | Gets the HTTP status code for the request. |
The Success field is generally used to indicate success/failure of a query request and if it has failed, the Errors, Warnings, Status and HttpStatusCode fields can be used to diagnose the error/issue so that it can be resolved.
Using Prepared Statements
Every statement executed by the server must have an associated query plan created which takes time and resources. Prepared statements allow the generated query plan to reused over and over again improving overall performance and server health. To use prepared statements, the AdHoc parameter must be set to false:
1 2 3 4 5 6 |
var queryRequest = new QueryRequest() .Statement("SELECT * FROM `beer-sample` LIMIT 1") .AdHoc(false); var result = bucket.Query<dynamic>(queryRequest); |
The SDK will handle the preparation, caching and reuse of the prepared statement for this statement and all future requests with it.
Multi-dimensional scaling
Also, known as MDS, Multi-dimensional scaling is a new feature of Couchbase Server 4.0 that allows you assign the services you wish to run on a particular node in your cluster. For instance, if you have a four node cluster, one node could be dedicated to queries, one node dedicated to indexing and two nodes dedicated to the data service – or some combination services can run on a particular node. The SDK itself is aware of which nodes support the respective services and will route queries and keys to the correct node in the cluster “automagically”
Enhanced Durability
Enhanced durability is a new way enforcing durability constraints. Durability constraints are constraints that you place on a mutation to ensure that a key has been replicated and/or persisted to “n” replicas. The big difference between the older “observe” based durability constraints is that the enhanced durability uses a replica sequence number to determine if a key has achieved the desired ability, while “observe” compares the replica state of the key itself.
Enhanced durability is not a change to the existing key/value API's, but instead is simply a configuration change; the rest is done “under the hood” by the internal APIs of the SDKs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var config = new ClientConfiguration { BucketConfigs = new Dictionary<string, BucketConfiguration> { { "default", new BucketConfiguration { UseEnhancedDurability = true } } } }; using (var cluster = new Cluster(config)) { using (var bucket = cluster.OpenBucket()) { var result = bucket.Insert(key, "foo", ReplicateTo.Two, PersistTo.Two); if(result.Success) { //do stuff if successful } } } |
The key here is that UseEnhancedDurability was set to true on the BucketConfiguration for the “default” bucket. The external durability API is the same; the PersistTo and ReplicateTo fields are passed in along with the key and value with the desired durability which must be met before the operation will return.
GEO Spatial Views
A new twist to an old Couchbase server is support for GEO Spatial views. GEO spatial views are created by defining Map functions using JavaScript (reduce is not supported) that allow you to query geographic ranges.
Here is an example of a Spatial View:
1 2 3 4 5 6 |
function (doc) { if (doc.type == "brewery" && doc.geo.lon && doc.geo.lat) { emit({ "type": "Point", "coordinates": [doc.geo.lon, doc.geo.lat]}, null); } } |
The SDK API for using Spatial Views is very similar to the View API, but uses a special SpatialViewQuery class to construct an object which is then submitted to the CouchbaseBucket class for execution.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var query = new SpatialViewQuery().From("beer_ext_spatial", "points") .Stale(StaleState.False) .StartRange(-10.37109375, 33.578014746143985) .EndRange(43.76953125, 71.9653876991313) .ConnectionTimeout(60000) .Limit(1) .Skip(0); var result = bucket.Query<dynamic>(query); if(result.Success) { foreach(var row in result.Rows) { Console.Writeline(row); } } |
You can learn more about writing Spatial Views here.
How to get it
The SDK is available for download directly, through NuGet, or by cloning and pulling the Github repo: